#ifndef TOKEN_H
#define TOKEN_H

#include "Symbol.h"

class Scope;


struct SimpleToken
{
	enum Type
	{
		Name,
		Integer,
		Float,
		String,
		Char,
		Operator
	};

	SimpleToken(Type type, const char* value, int from, int to, int line)
	{
		this->type = type;
		this->value = value;
		this->from = from;
		this->to = to;
		this->line = line;
	}

	void error(string err)
	{
		cout << line + ":" + err + " " + value; 
	}

	Type type;
	int from;
	int to;
	int line;
	const char* value;
};

struct Token
{
	enum Arity
	{
		Unary,
		Binary,
		Ternary,
		Literal,
		Operator,
		Name,
		This,
		Super,
		Statement,
		Function,
		Class,
		Constructor
	};

    Token()
    {
        prototype = NULL;
        value = "";
        first = NULL;
        second = NULL;
        third = NULL;
		scope = NULL;
		parent = NULL;
		leaves = new vector<Token*>();
    }

    Token(Symbol* s)
    {
        prototype = s;
        value = s->id.c_str();
        first = NULL;
        second = NULL;
        third = NULL;
        leaves = NULL;
		scope = NULL;
		parent = NULL;
    }

	void setFirst(Token* t)
	{
		t->parent = this;
		first = t;
	}

	void setSecond(Token* t)
	{
		t->parent = this;
		second = t;
	}

	void setThird(Token* t)
	{
		t->parent = this;
		third = t;
	}

    Token* first;
	Token* second;
	Token* third;
	Token* parent;

    Symbol* prototype;
	Scope* scope;

    const char* value;
    vector<Token*>* leaves;

    Arity arity;
	int line;

	

    void error(string err)
	{
		string e = "line ";
		char n[10];
		sprintf(n,"%d:",line);
		e += n;
		e += value;
		e += " " + err;
		throw e;
	}

	void warning(string err)
	{
		cout << line + ":" + err; 
	}


    inline Token* nud()
	{
		return prototype->nud(this);
	}

    inline Token* led(Token* left)
	{
		return prototype->led(this,left);
	}

	inline Token* std()
	{
		return prototype->std(this);
	}

	inline string compile()
	{
		if (prototype)
		{
			return prototype->compile(this);
		}
		else if (leaves)
		{
			string code;
			for (unsigned i = 0; i < leaves->size(); i++)
			{
				code += leaves->at(i)->compile();
				code += ";\n";
			}
			return code;
		}
		return value;
	}

	inline string compileDef()
	{
		if (prototype)
		{
			return prototype->compileDef(this);
		}
		return "";
	}

    inline void push(Token* s)
	{
        if (!leaves)
        {
            leaves = new vector<Token*>();
        }
		leaves->push_back(s);
		s->parent = parent;
	}

    inline string& id()
    {
        return prototype->id;
    }

	inline Symbol::Type type()
	{
		return prototype->type(this);
	}

	void setType(Symbol::Type t);
	void setType(Token* n);
	string refType();

    ~Token()
    {
        if (first) delete first;
        if (second) delete second;
        if (third) delete third;
        if (leaves) delete leaves;
    }
};

#endif